package com.szm.mvc.filter;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.json.JSONException;
import org.json.JSONObject;

import com.szm.business.util.SysConstant;
import com.szm.mvc.action.base.ActionSupport;
import com.szm.mvc.util.BeanUtils;
import com.szm.mvc.util.ConfigManager;
import com.szm.mvc.util.Constant;
import com.szm.mvc.util.bean.ActionBean;
import com.szm.mvc.util.bean.PackageBean;
import com.szm.mvc.util.bean.ResultsBean;

/**
 *@Author szm
 *@ClassName ActionFilter.java
 *@Description 模拟 struts2中的 StrutsPrepareAndExecuteFilter
 *             实现对Request请求的拦截
 *@Date 2015年11月24日
 */
public class ActionFilter implements Filter {
	
	/**
	 * CONFIG 获取 init-param
	 */
	private static final String PARAM_NAME = "config";
	/**
	 * PARAM_VALUE 获取 init-param
	 */
	private static String PARAM_VALUE = "";
	
	/**
	 * EXTENSION 模拟struts.action.extension,默认后缀名.
	 * 默认为 do、action、或者空 
	 */
	private static String [] EXTENSIONS = {"do","action",""};
	
	@Override
	public void destroy() {
		
	}

	/* 对所有请求做处理：拦截、action参数自动注入、请求跳转
	 * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
	 */
	@Override
	public void doFilter(ServletRequest req, ServletResponse resp,
			FilterChain chain) throws IOException, ServletException {
		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) resp;
		// 获得当前访问的路径
		String urlPath = request.getServletPath();
		boolean filterFlag = false; 
		boolean defaultFilterFlag = false;
		//第一判断：EXTENSIONS 存在 "" ，则不带后缀也能匹配，但是如果url含有.后缀名则不受此约束
		for(String extension : EXTENSIONS){
			if((null == extension || "".equals(extension)) && !urlPath.contains(".")){
				defaultFilterFlag = true;
			}
		}
		//urlPath 包含后缀，判断是否匹配 后缀
		if(!defaultFilterFlag){
			String urlExtension = urlPath.substring(urlPath.lastIndexOf(".")+1);
			//第二判断：urlPath是否匹配 EXTENSIONS中其他选项
			for(String extension : EXTENSIONS){
				if(urlExtension.equals(extension)){
					filterFlag = true;
					//匹配后，去除
					urlPath = urlPath.replaceAll("\\."+extension, "");
					break;
				}
			}
		}
		if(defaultFilterFlag || filterFlag){
			//查找 actionBean
			ActionBean actionBean = findActionBean(request, response, urlPath);
			//执行 action
			executeAction(request, response, actionBean);
		}else{
			chain.doFilter(request, response);
		}
	}

	/* 系统初始化,解析 struts.xml 获取 action配置信息(class、result)
	 * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
	 */
	@Override
	public void init(FilterConfig config) throws ServletException {
		PARAM_VALUE = config.getInitParameter(PARAM_NAME);
		ServletContext servletContext = config.getServletContext();
		//得到文件 路径
		String filePath = servletContext.getRealPath("/WEB-INF/classes/"+PARAM_VALUE);
		File configFile = new File(filePath);
		ConfigManager configManager = new ConfigManager();
		configManager.init(configFile);
		//存放至  ServletContext
		servletContext.setAttribute(Constant.PACKAGE, configManager.packageBeanMap);
		
	}
	
	/**
	 * @author szm
	 * @Description 执行Action
	 * @param request
	 * @param response
	 * @param actionBean
	 */
	@SuppressWarnings("unchecked")
	public void executeAction(HttpServletRequest request, HttpServletResponse response, ActionBean actionBean){
		try {
		RequestDispatcher dispatcher = null;
		if(null == actionBean){
			response.sendRedirect(request.getContextPath()+Constant.DEFAULTNAMESPACE);
			return;
		}
		
		// 获得请求参数
		Map<String,Object> parameterMap = request.getParameterMap();
		
		// 解析 actionBean
		String nameStr = actionBean.getName();
		String urlStr = actionBean.getUrl();
		String clazzStr = actionBean.getClazz();
		String methodStr = actionBean.getMethod();
		
		//获取包空间
		PackageBean packageBean = actionBean.getPackageBean();
		if(null == packageBean){
			response.sendRedirect(request.getContextPath()+Constant.DEFAULTNAMESPACE);
			return;
		}
		//获取默认返回值
		List<ResultsBean> globalResults = packageBean.getGlobalResults();
		
		//获取这个action 的返回值
		List<ResultsBean> resultsBeanList = actionBean.getResultsBeanList();
		
		//默认跳转至 /
		String dispatcherUrl = Constant.DEFAULTNAMESPACE;
		//默认跳转 方式
		String dispatcherType = Constant.DISPATCHER;
		//返回值
		String dispatcherName = "";
		//变量 判断是匹配 dispatcherUrl、dispatcherType
		boolean flag = false;
		//返回值参数
		Map<String, String> resultsParam = null;
		
		//反射  初始化------------
		Class<?> clazz = null;
		ActionSupport action = null;
		
		if(null == clazzStr || "".equals(clazzStr)){
			//第一种  只有name,不指定 class
			//判断是否有 Results 
			if(null != resultsBeanList && resultsBeanList.size()>0 && !flag){
				for(ResultsBean resultsBean : resultsBeanList){
					dispatcherUrl = resultsBean.getUrl();
					dispatcherType = resultsBean.getType();
					resultsParam = resultsBean.getParam();
					flag = true;
					break;
				}
			}
		}else if(null == methodStr || "".equals(methodStr)){
			//第二种  有name,指定 class,无 mathod
			clazz = Class.forName(clazzStr);
			action = (ActionSupport) clazz.newInstance();
			//反射  赋值  ------------
			for(Entry<String, Object> entry : parameterMap.entrySet()){
				/*String key = entry.getKey();
				Field field = null;
				// 判断传入的属性是否带".",带"."说明该属性是一个关联到其他类的属性,于是要获得其最终的属性的类型
				if (key.contains(".")) {
					String[] realProperties = key.split(".");
					field = clazz.getDeclaredField(realProperties[0]);
				} else {
					field = clazz.getDeclaredField(entry.getKey());
				}
				if(null == field){
					continue;
				}
				field.setAccessible(true);
			    //赋值
				fieldMapper(action, key,entry.getValue());
				field.setAccessible(false);*/
				
				fieldMapper(action, entry.getKey(),entry.getValue());
				
			}
			//执行默认方法
			dispatcherName = action.execute();
			
			ResultsBean resultsBean = parseResultBean(globalResults, resultsBeanList, dispatcherName, flag);
			
			dispatcherUrl = resultsBean.getUrl();
			dispatcherType = resultsBean.getType();
			resultsParam = resultsBean.getParam();
			
		}else if(methodStr.contains(Constant.LEFT_BRACKET) && methodStr.contains(Constant.RIGHT_BRACKET)){
			//第四种：通配符
			int nameIndex = Integer.valueOf(methodStr.replaceAll("\\"+Constant.LEFT_BRACKET, "").replaceAll("\\"+Constant.RIGHT_BRACKET, "").trim());
			
			String [] urls = urlStr.split(nameStr.replaceAll("\\"+Constant.WILDCARD, ""));
			
			clazz = Class.forName(clazzStr);
			action = (ActionSupport) clazz.newInstance();
			
			for(Entry<String, Object> entry : parameterMap.entrySet()){
				fieldMapper(action, entry.getKey(),entry.getValue());
			}
			
			Method method = action.getClass().getDeclaredMethod(urls[nameIndex]);
			dispatcherName = (String) method.invoke(action);
			
			ResultsBean resultsBean = parseResultBean(globalResults, resultsBeanList, dispatcherName, flag);
			
			dispatcherUrl = resultsBean.getUrl();
			dispatcherType = resultsBean.getType();
			resultsParam = resultsBean.getParam();
			
		}else{
			//第三种：指定class与Method
			clazz = Class.forName(clazzStr);
			action = (ActionSupport) clazz.newInstance();
			for(Entry<String, Object> entry : parameterMap.entrySet()){
				fieldMapper(action, entry.getKey(),entry.getValue());
			}
			Method method = action.getClass().getDeclaredMethod(methodStr);
			dispatcherName = (String) method.invoke(action);
			
			ResultsBean resultsBean = parseResultBean(globalResults, resultsBeanList, dispatcherName, flag);
			
			dispatcherUrl = resultsBean.getUrl();
			dispatcherType = resultsBean.getType();
			resultsParam = resultsBean.getParam();
			
		}
		
		if(null != dispatcherName && "" != dispatcherName && null != resultsParam && resultsParam.size() >0){
			String rootJsonField = resultsParam.get(Constant.ROOT);
			String contentType = resultsParam.get(Constant.CONTENTTYPE);
			if(null == contentType){
				contentType = "text/html";
			}
			
			JSONObject resultJson=new JSONObject();
			Object fieldObj =  BeanUtils.forceGetProperty(action, rootJsonField);
			resultJson.accumulate(rootJsonField, fieldObj);
			
			//response.setContentType(contentType+";charset=GBK");
			response.setContentType(contentType);
			
			PrintWriter out = response.getWriter();
			out.print(resultJson);
			out.flush();
			out.close();
			
		}else if(null != dispatcherUrl ){
			//默认 跳转方式
			if(null == dispatcherType || "".equals(dispatcherType) || Constant.DISPATCHER.equals(dispatcherType)){
				dispatcher = request.getRequestDispatcher(dispatcherUrl);
				dispatcher.forward(request, response);
			}else if(Constant.REDIRECT.equals(dispatcherType)){
				if(0==dispatcherUrl.indexOf("/")){
					response.sendRedirect(request.getContextPath()+dispatcherUrl);
				}else{
					response.sendRedirect(request.getContextPath()+"/"+dispatcherUrl);
				}
			}else if(Constant.CHAIN.equals(dispatcherType)){
				dispatcher = request.getRequestDispatcher(dispatcherUrl);
				dispatcher.forward(request, response);
			}else if(Constant.REDIRECTACTION.equals(dispatcherType)){
				response.sendRedirect(request.getContextPath()+"/"+dispatcherUrl);
			}
		}else{
			//跳转回主页
			response.sendRedirect(request.getContextPath()+Constant.DEFAULTNAMESPACE);
		}
		
		}catch(Exception e){
			System.out.println(SysConstant.SYS_FAIL+"执行Action出错!");
		}
		

	}
	
	/**
	 * @author szm
	 * @Description 
	 * @param request
	 * @param response
	 * @param urlPath
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public ActionBean findActionBean(HttpServletRequest request, HttpServletResponse response, String urlPath){
		
		Map<String,PackageBean> packageBeanMap = (Map<String, PackageBean>)(request.getSession().getServletContext().getAttribute(Constant.PACKAGE));
		ActionBean actionBean = new ActionBean();
		// 获得当前访问的路径
		//String urlPath = request.getServletPath();
		//去除 ;
		int indexOfSemicolon = urlPath.indexOf(Constant.SEMICOLO);
		urlPath = (indexOfSemicolon > -1) ? urlPath.substring(0, indexOfSemicolon) : urlPath;
		//获取命名空间     / 默认空间
		
		String namespace,name;
		//最后的斜杆的位置 /
		int lastSlash = urlPath.lastIndexOf(Constant.DEFAULTNAMESPACE); 
		if(lastSlash == -1){
			namespace = "";
			name = urlPath;
		}else if(lastSlash == 0){
			namespace = Constant.DEFAULTNAMESPACE;
			name = urlPath.substring(lastSlash+1);
		}else{
			//临时的命名空间，将会用来进行匹配
			String prefix = urlPath.substring(0,lastSlash);
			//将命名空间暂时设为""
			namespace = "";
			//作用是判断配置文件中是否配置了命名空间"/"
			boolean rootAvailable = false;
			for(Entry<String, PackageBean> entry : packageBeanMap.entrySet()){
				PackageBean packageBean = entry.getValue();
				String ns = packageBean.getNamespace();
				if(ns != null && prefix.startsWith(ns) && (prefix.length() == ns.length() || String.valueOf(prefix.charAt(ns.length())).equals(Constant.DEFAULTNAMESPACE) )){
					if (ns.length() > namespace.length()) {
						 namespace = ns;
					}
				}
				if(Constant.DEFAULTNAMESPACE.equals(ns)){
					rootAvailable = true;
				}
			}
			
			name = urlPath.substring(namespace.length()+1);
			
			if(rootAvailable && "".equals(namespace)){
				namespace = Constant.DEFAULTNAMESPACE;
			}
		}
		
		//根据 查找 PackageBean
		PackageBean packageBean = null;
		Map<String,ActionBean> actionBeanMap = null;
		for(Entry<String, PackageBean> entry : packageBeanMap.entrySet()){
			packageBean = entry.getValue();
			String ns = packageBean.getNamespace();
			//默认命名空间
			if(namespace.equals(ns) || ns == null || "".equals(ns) ){
				actionBeanMap = packageBean.getActionBeanMap();
			}
		}
		
		//遍历 actionBeanMap
		for(Entry<String, ActionBean> entry : actionBeanMap.entrySet()){
			String key = entry.getKey();
			if(name.equals(key)){
				actionBean = entry.getValue();
				continue;
			}
			
			//通配符 处理
			String method = entry.getValue().getMethod();
			if(!(key.contains(Constant.WILDCARD) && null != method && method.contains(Constant.LEFT_BRACKET) && method.contains(Constant.RIGHT_BRACKET)) ){
				continue;
			}
			
			key = key.replaceAll("\\"+Constant.WILDCARD, "\\."+"\\"+Constant.WILDCARD);
			String m = "^"+key+"$";
			if(name.matches(m)){
				actionBean.setName(entry.getValue().getName());
				actionBean.setUrl(name);
				actionBean.setMethod(method);
				actionBean.setClazz(entry.getValue().getClazz());
				actionBean.setPackageBean(entry.getValue().getPackageBean());
				actionBean.setResultsBeanList(entry.getValue().getResultsBeanList());
			}
		}
		return actionBean;
	}
	
	/**
	 * @author szm
	 * @Description 给字段赋值
	 * @param field
	 * @param object
	 * @param value
	 */
	public void fieldMapper(Object object, String properties, Object value) throws Exception{
		//暂时 不考虑 数组类型、集合类型 属性的自动注入
		Object valueStr = null;
		if(value instanceof Object[]){
			valueStr = ((Object[])value)[0];
		}else{
			valueStr = value;
		}
		if(properties.contains(".")){
			String[] realProperties = properties.split("\\.");
			//遍历 从 0 开始，循环赋值属性的值 . 例如    school.user.name
			//Field field = object.getClass().getDeclaredField(realProperties[0]);
			Field field = BeanUtils.getDeclaredField(object, realProperties[0]);
			if(null == field){
				return;
			}
			field.setAccessible(true);
			Class<?> propertiesType = field.getType();
			String propertiesKey = properties.substring(properties.indexOf(".")+1);
			Object typeObj =  field.get(object);
			if(null == typeObj){
				typeObj = propertiesType.newInstance();
			}
			field.set(object, typeObj);
			fieldMapper(typeObj, propertiesKey, value);
			field.setAccessible(false);
			
			//以下是 精简版,但强悍
			/*Class propertiesType = BeanUtils.getPropertyType(object, realProperties[0]);
			String propertiesKey = properties.substring(properties.indexOf(".")+1);
			Object typeObj = BeanUtils.forceGetProperty(object, realProperties[0]);
			if(null == typeObj){
				typeObj = propertiesType.newInstance();
			}
			BeanUtils.forceSetProperty(object, realProperties[0], typeObj);
			fieldMapper(typeObj, propertiesKey, value);*/
			
		}else{
			//Field field = object.getClass().getDeclaredField(properties);
			Field field = BeanUtils.getDeclaredField(object, properties);
			field.setAccessible(true);
			Class<?> type = field.getType();
			if(type==String.class){
				field.set(object, valueStr.toString());
			}else if(type==Integer.class || type==int.class){
				field.set(object, Integer.parseInt(valueStr.toString()));
			}else if(type==Long.class){
				field.set(object, Long.valueOf(valueStr.toString()));
			}else if(type==Boolean.class){
				field.set(object, Boolean.valueOf(valueStr.toString()));
			}else if(type==BigDecimal.class){
				field.set(object, new BigDecimal(valueStr.toString()));
			}else if(type==Double.class){
				field.set(object, Double.valueOf(valueStr.toString()));
			}
			field.setAccessible(false);
		}
	}
	
	/**
	 * @Author szm
	 * @Description 解析 resultsBean
	 * @param globalResults 默认返回值
	 * @param resultsBeanList action 的返回值
	 * @param dispatcherUrl 跳转url
	 * @param dispatcherType 跳转 方式
	 * @param dispatcherName 返回值
	 * @param flag 判断是匹配 dispatcherUrl、dispatcherType
	 */
	public ResultsBean parseResultBean(List<ResultsBean> globalResults, List<ResultsBean> resultsBeanList, String dispatcherName, boolean flag){
		ResultsBean results = null;
		if(null != resultsBeanList && resultsBeanList.size()>0 && !flag){
			for(ResultsBean resultsBean : resultsBeanList){
				String name = resultsBean.getName();
				if(null != name && dispatcherName.equals(name)){
					flag = true;
					results = resultsBean;
					break;
				}
				
			}
		}
		if(null != globalResults && globalResults.size()>0 && !flag){
			for(ResultsBean resultsBean : globalResults){
				String name = resultsBean.getName();
				if(null != name && dispatcherName.equals(name)){
					results = resultsBean;
					flag = true;
					break;
				}
			}
		}
		return results;
	}

}
